﻿using System.Collections.Generic;
using Cms.Application.Admin.Presenters;
using Cms.Application.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Cms.Domain;
using Cms.Application.Admin.Views;
using Moq;

namespace Cms.Tests.Unit.Application.Admin.Presenters
{
    [TestClass]
    public class EditMediaFolderPresenterTests : BasePresenterTests
    {
        private Mock<IEditMediaFolderView> _viewMock;
        private Site _site;
        
        private void Prepare()
        {
            InitRepositoryFactoryMock();
            _site = new Site { Domain = "www.fredium.com", RootMediaFolder = "media" };
            Administrator admin = new Administrator { CurrentSite = _site };
            AdministratorRepositoryMock.Setup(a => a.GetAdministrator(1)).Returns(admin);
            IList<Site> sites = new List<Site> { _site };
            SiteRepositoryMock.Setup(s => s.GetSites()).Returns(sites);
            _viewMock = new Mock<IEditMediaFolderView>();
            _viewMock.SetupAllProperties();
            _viewMock.Setup(v => v.AdministratorID).Returns(1);
            _viewMock.Setup(v => v.Localization).Returns("en-US");
            _viewMock.Setup(v => v.ContextTranslationFile).Returns("Admin/Translations/EditMediaFolder.xml");
        }

        [TestMethod]
        public void Initialize_NewFolder_Success()
        {
            Prepare();
            _viewMock.Setup(v => v.FolderID).Returns(-1);
            EditMediaFolderPresenter presenter = new EditMediaFolderPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.Initialize();
            Assert.AreEqual(-1, _viewMock.Object.SelectedParentFolderID);
        }

        [TestMethod]
        public void Initialize_ExistingRootFolder_Success()
        {
            Prepare();
            _viewMock.Setup(v => v.FolderID).Returns(1);
            MediaFolder folder = new MediaFolder { ID = 1, Name = "TheB", Site = _site };
            MediaRepositoryMock.Setup(m => m.GetMediaFolder(1)).Returns(folder);
            EditMediaFolderPresenter presenter = new EditMediaFolderPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.Initialize();
            Assert.AreEqual(-1, _viewMock.Object.SelectedParentFolderID);
            Assert.AreEqual("TheB", _viewMock.Object.Name);
        }

        [TestMethod]
        public void Initialize_ExistingSubFolder_Success()
        {
            Prepare();
            _viewMock.Setup(v => v.FolderID).Returns(1);
            MediaFolder parent = new MediaFolder { ID = 10, Name = "TheParent", Site = _site };
            MediaFolder folder = new MediaFolder { ID = 1, Name = "TheB", Site = _site, Parent = parent };
            parent.SubFolders.Add(folder);
            MediaRepositoryMock.Setup(f => f.GetMediaFolder(1)).Returns(folder);
            EditMediaFolderPresenter presenter = new EditMediaFolderPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.Initialize();
            Assert.AreEqual(10, _viewMock.Object.SelectedParentFolderID);
        }

        [TestMethod]
        public void Save_NoFolderName_Error()
        {
            Prepare();
            _viewMock.Setup(v => v.FolderID).Returns(-1);
            EditMediaFolderPresenter presenter = new EditMediaFolderPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.Initialize();
            _viewMock.Setup(v => v.Name).Returns("");
            presenter.Save();
            Assert.IsFalse(presenter.IsValid);
            Assert.IsTrue(presenter.ValidationErrorCodes.Contains(1)); // 1 = NoNameError
        }

        private void TestInvalidFolderName(EditMediaFolderPresenter presenter, string name)
        {
            presenter.ClearErrors();
            _viewMock.Setup(v => v.Name).Returns(name);
            presenter.Save();
            Assert.IsTrue(presenter.ValidationErrorCodes.Contains(2)); // 2 = ReservedNameError
        }

        [TestMethod]
        public void Save_SelfAsParentFolder_Error()
        {
            Prepare();
            MediaFolder folder = new MediaFolder { ID = 22, Name = "Beer", Site = _site };
            MediaRepositoryMock.Setup(f => f.GetMediaFolder(22)).Returns(folder);
            _viewMock.Setup(v => v.FolderID).Returns(22);
            EditMediaFolderPresenter presenter = new EditMediaFolderPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.Initialize();
            _viewMock.Setup(v => v.Name).Returns("Beer"); //This is needed to get passed to the line I want to test
            _viewMock.Setup(v => v.SelectedParentFolderID).Returns(22);
            presenter.Save();
            Assert.IsTrue(presenter.ValidationErrorCodes.Contains(3)); // 3 = SaveInSelfError
        }

        [TestMethod]
        public void Save_SubfolderAsParent_Error()
        {
            Prepare();
            // Setup a folder structure like this: domain/A/B/C
            MediaFolder folderA = new MediaFolder { ID = 1, Name = "A", Site = _site };
            MediaFolder folderB = new MediaFolder { ID = 2, Name = "B", Site = _site };
            MediaFolder folderC = new MediaFolder { ID = 3, Name = "C", Site = _site };
            folderB.Parent = folderA;
            folderC.Parent = folderB;
            MediaRepositoryMock.Setup(f => f.GetMediaFolder(1)).Returns(folderA);
            MediaRepositoryMock.Setup(f => f.GetMediaFolder(3)).Returns(folderC);
            _viewMock.Setup(v => v.FolderID).Returns(1); // Edit folder A
            EditMediaFolderPresenter presenter = new EditMediaFolderPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.Initialize();
            _viewMock.Setup(v => v.Name).Returns("A");
            _viewMock.Setup(v => v.SelectedParentFolderID).Returns(3); // Try to save with folderC as parent
            presenter.Validate();
            Assert.IsTrue(presenter.ValidationErrorCodes.Contains(4)); // 4 = SaveInChildError
        }

        [TestMethod]
        public void Save_NamingConflict_Error()
        {
            Prepare();
            MediaFolder folderA = new MediaFolder { Site = _site, ID = 1, Name = "folderA" };
            _site.MediaFolders.Add(folderA);
            MediaFolder folderB = new MediaFolder { Site = _site, ID = 2, Name = "folderB" };
            _site.MediaFolders.Add(folderB);
            // Try to rename folderB to folderA.
            MediaRepositoryMock.Setup(f => f.GetMediaFolder(2)).Returns(folderB);
            _viewMock.Setup(v => v.FolderID).Returns(2); // Test on folder B
            EditMediaFolderPresenter presenter = new EditMediaFolderPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.Initialize();
            _viewMock.Setup(v => v.Name).Returns("folderA");
            _viewMock.Setup(v => v.SelectedParentFolderID).Returns(-1); // Try to save in root
            presenter.Save();
            Assert.IsTrue(presenter.ValidationErrorCodes.Contains(5)); // 5 = NameCollisionError
        }

        [TestMethod]
        public void CollectOriginalPathsForUpdate()
        {
            Prepare();
            MediaFolder folderA = new MediaFolder { Site = _site, ID = 1, Name = "folderA" };
            _site.MediaFolders.Add(folderA);
            MediaFolder folderB = new MediaFolder { Site = _site, ID = 2, Name = "folderB", Parent = folderA };
            _site.MediaFolders.Add(folderB);
            folderA.SubFolders.Add(folderB);
            MediaFile file1 = new MediaFile { ID = 1, Name = "page1.aspx", Folder = folderA, Site = _site };
            MediaFile file2 = new MediaFile { ID = 2, Name = "page2.aspx", Folder = folderB, Site = _site };
            folderA.Files.Add(file1);
            folderB.Files.Add(file2);
            _viewMock.Setup(v => v.FolderID).Returns(1);
            EditMediaFolderPresenter presenter = new EditMediaFolderPresenter(_viewMock.Object, FactoryMock.Object);
            IDictionary<int, string> originalPaths = new Dictionary<int, string>();
            presenter.CollectOriginalPathsForUpdate(folderA, ref originalPaths);
            Assert.AreEqual(2, originalPaths.Count);
            Assert.IsTrue(originalPaths.Contains(new KeyValuePair<int, string>(1, "media/folderA/page1.aspx")));
            Assert.IsTrue(originalPaths.Contains(new KeyValuePair<int, string>(2, "media/folderA/folderB/page2.aspx")));
        }

        [TestMethod]
        public void Save_SetName_Success()
        {
            Prepare();
            MediaFolder folderA = new MediaFolder { Site = _site, ID = 1, Name = "folderA" };
            _site.MediaFolders.Add(folderA);
            _viewMock.Setup(v => v.FolderID).Returns(1);
            _viewMock.Setup(v => v.Name).Returns("updatedName");
            MediaRepositoryMock.Setup(f => f.GetMediaFolder(1)).Returns(folderA);
            EditMediaFolderPresenter presenter = new EditMediaFolderPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.DiskAccess = new Mock<IDiskAccess>().Object; // The Save method requires an IO object
            presenter.Save();
            Assert.AreEqual("updatedName", folderA.Name);
        }

        [TestMethod]
        public void Save_SetParentFolder_Success()
        {
            Prepare();
            MediaFolder folderA = new MediaFolder { Site = _site, ID = 1, Name = "folderA" };
            MediaFolder folderB = new MediaFolder { Site = _site, ID = 2, Name = "folderB" };
            _site.MediaFolders.Add(folderA);
            _site.MediaFolders.Add(folderB);
            MediaRepositoryMock.Setup(f => f.GetMediaFolder(1)).Returns(folderA);
            MediaRepositoryMock.Setup(f => f.GetMediaFolder(2)).Returns(folderB);
            _viewMock.Setup(v => v.FolderID).Returns(1);
            _viewMock.Setup(v => v.Name).Returns("folderA"); // Needed to pass validation
            _viewMock.Setup(v => v.SelectedParentFolderID).Returns(2);
            EditMediaFolderPresenter presenter = new EditMediaFolderPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.DiskAccess = new Mock<IDiskAccess>().Object; // The Save method requires an IO object
            presenter.Save();
            Assert.AreSame(folderB, folderA.Parent);
        }

        [TestMethod]
        public void Save_NewFolder_CreateFolderIsCalled()
        {
            Prepare();
            _viewMock.Setup(v => v.FolderID).Returns(-1);
            _viewMock.Setup(v => v.Name).Returns("SomeFolder");
            EditMediaFolderPresenter presenter = new EditMediaFolderPresenter(_viewMock.Object, FactoryMock.Object);
            Mock<IDiskAccess> io = new Mock<IDiskAccess>();
            presenter.DiskAccess = io.Object;
            presenter.Save();
            // Check that CreateFolder was called correctly:
            io.Verify(o => o.CreateDirectory("media\\SomeFolder"));
        }

        [TestMethod]
        public void Save_EditFolder_MoveFolderIsCalled()
        {
            Prepare();
            MediaFolder folderA = new MediaFolder { Site = _site, ID = 1, Name = "folderA" };
            MediaFolder folderB = new MediaFolder { Site = _site, ID = 2, Name = "folderB" };
            _site.MediaFolders.Add(folderA);
            _site.MediaFolders.Add(folderB);
            _viewMock.Setup(v => v.FolderID).Returns(1);
            _viewMock.Setup(v => v.Name).Returns("folderA"); // Needed to pass validation
            _viewMock.Setup(v => v.SelectedParentFolderID).Returns(2);
            MediaRepositoryMock.Setup(f => f.GetMediaFolder(1)).Returns(folderA);
            MediaRepositoryMock.Setup(f => f.GetMediaFolder(2)).Returns(folderB);
            EditMediaFolderPresenter presenter = new EditMediaFolderPresenter(_viewMock.Object, FactoryMock.Object);
            Mock<IDiskAccess> io = new Mock<IDiskAccess>();
            presenter.DiskAccess = io.Object;
            presenter.Save();
            // Check that CreateFolder was called correctly:
            io.Verify(o => o.MoveDirectory("media\\folderA", "media\\folderB\\folderA"));
        }

        [TestMethod]
        public void IsInCategory_ExistingFolder_Success()
        {
            Prepare();
            MediaFolder folderA = new MediaFolder { Site = _site, ID = 1, Name = "folderA" };
            Category pc = new Category() { Name = "Cars" };
            pc.MediaFolders.Add(folderA);
            folderA.Categories.Add(pc);
            MediaRepositoryMock.Setup(f => f.GetMediaFolder(1)).Returns(folderA);
            _viewMock.Setup(v => v.FolderID).Returns(1);
            _viewMock.Setup(v => v.Name).Returns("SomeFolder");
            EditMediaFolderPresenter presenter = new EditMediaFolderPresenter(_viewMock.Object, FactoryMock.Object);
            Mock<IDiskAccess> io = new Mock<IDiskAccess>();
            presenter.DiskAccess = io.Object;
            Assert.IsTrue(presenter.IsInCategory(pc));
        }

        [TestMethod]
        public void IsInInheritedCategory_No_False()
        {
            Prepare();
            MediaFolder folderA = new MediaFolder { Site = _site, ID = 1, Name = "folderA" };
            Category pc = new Category() { Name = "Cars" };
            pc.MediaFolders.Add(folderA);
            folderA.Categories.Add(pc);
            MediaRepositoryMock.Setup(f => f.GetMediaFolder(1)).Returns(folderA);
            _viewMock.Setup(v => v.FolderID).Returns(1);
            _viewMock.Setup(v => v.Name).Returns("SomeFolder");
            EditMediaFolderPresenter presenter = new EditMediaFolderPresenter(_viewMock.Object, FactoryMock.Object);
            Mock<IDiskAccess> io = new Mock<IDiskAccess>();
            presenter.DiskAccess = io.Object;
            Assert.IsFalse(presenter.IsInInheritedCategory(pc));
        }

        [TestMethod]
        public void IsInInheritedCategory_Yes_True()
        {
            Prepare();
            MediaFolder folderA = new MediaFolder { Site = _site, ID = 1, Name = "folderA" };
            MediaFolder folderB = new MediaFolder { Site = _site, ID = 2, Name = "folderB" };
            Category pc = new Category() { Name = "Cars" };
            pc.MediaFolders.Add(folderB);
            folderB.Categories.Add(pc);
            folderB.SubFolders.Add(folderA);
            folderA.Parent = folderB;
            MediaRepositoryMock.Setup(f => f.GetMediaFolder(1)).Returns(folderA);
            _viewMock.Setup(v => v.FolderID).Returns(1);
            _viewMock.Setup(v => v.Name).Returns("SomeFolder");
            EditMediaFolderPresenter presenter = new EditMediaFolderPresenter(_viewMock.Object, FactoryMock.Object);
            Mock<IDiskAccess> io = new Mock<IDiskAccess>();
            presenter.DiskAccess = io.Object;
            Assert.IsTrue(presenter.IsInInheritedCategory(pc));
        }
    }
}
